package jupitermouse.site.juc.demo.aqs.locksupport;

import java.util.concurrent.locks.LockSupport;

/**
 * <p>
 *  线程可以通过 Thread.sleep()、Object.wait()、LockSupport.park()方法 进入休眠。
 *  1. Thread.sleep()
 *  Thread.sleep(time)方法必须传入指定的时间，线程将进入休眠状态，通过jstack输出线程快照的话此时该线程的状态应该是TIMED_WAITING，表示休眠一段时间。
 *  通过sleep方法[进入休眠的线程不会释放持有的锁]，因此，在持有锁的时候调用该方法需要谨慎。
 *  2. Object.wait()
 *  如果不传timeout，wait将会进入无限制的休眠当中，直到有人唤醒他。使用wait()让线程进入休眠的话，无论有没有传入timeout参数，线程的状态都将是WAITING状态。
 *  再调用wait()方法后，线程进入休眠的同时，[会释放持有的该对象的锁]，这样其他线程就能在这期间获取到锁了。
 *  调用Object对象的notify()或者notifyAll()方法可以唤醒因为wait()而进入等待的线程。
 *  3. LockSupport.park()
 *  通过LockSupport.park()方法，我们也可以让线程进入休眠。它的底层也是调用了Unsafe类的park方法：
 *  other
 *  1. LockSupport.park()与wait的区别
 *  park、unpark方法和wait、notify()方法有一些相似的地方。都是休眠，然后唤醒。但是wait、notify方法有一个不好的地方，就是我们在编程的时候必须能保证wait方法比notify方法先执行。
 *  如果notify方法比wait方法晚执行的话，就会导致因wait方法进入休眠的线程接收不到唤醒通知的问题。而park、unpark则不会有这个问题，我们可以先调用unpark方法释放一个许可证，
 *  这样后面线程调用park方法时，发现已经许可证了，就可以直接获取许可证而不用进入休眠状态了。
 *  wait让线程阻塞前，必须通过synchronized获取同步锁。
 *  link: https://www.jianshu.com/p/58b711301615,https://www.cnblogs.com/Hermioner/p/9933389.html
 *  2. 使用注意
 *  因为wait需释放锁，所以必须在synchronized中使用（没有锁时使用会抛出IllegalMonitorStateException）
 *   a、notify也要在synchronized使用，并且应该指定对象
 *   b、synchronized(),wait(),notify() 对象必须一致，一个synchronized()代码块中只能有1个线程wait()或notify()
 *
 *   @author jupiter
 */
public class LockSupportDemo {
    public static void main(String[] args) throws InterruptedException {
//        waitNotifyMonitorStateException();
//        waitNotifyMonitorSuccess();
        lockSupportPark();
    }

    static void lockSupportTask(){

    }

    /**
     * <p>
     *     描述：线程A执行一段业务逻辑后调用wait阻塞住自己。主线程调用notify方法唤醒线程A，线程A然后打印自己执行的结果。
     *     结果：java.lang.IllegalMonitorStateException
     *     原因：wait和notify/notifyAll方法只能在同步代码块里用，参照：WaitNotifyMonitorSuccess
     * </p>
     * @throws InterruptedException
     */
    static void waitNotifyMonitorStateException() throws InterruptedException {
        Object waitObj = new Object();
        Thread A = new Thread(()->{
            int sum = 0;
            for (int i=0;i<10;i++){
                sum +=i;
            }
            try {
                waitObj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(sum);
        });
        A.start();
        // 主线程休眠1秒，保证计算完成
        Thread.sleep(1000);
        waitObj.notify();
    }

    /**
     * <p>
     *     WaitNotify 正确姿势
     * </p>
     * @throws InterruptedException
     */
    static void waitNotifyMonitorSuccess() throws InterruptedException {
        Object waitObj = new Object();
        Thread A = new Thread(()->{
            int sum = 0;
            for (int i=0;i<10;i++){
                sum +=i;
            }
            try {
                synchronized (waitObj){
                    System.out.println("waitObj wait ...");
                    waitObj.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(sum);
        });
        A.start();
        // 主线程休眠1秒，保证计算完成,阻塞在wait方法
        Thread.sleep(1000);
        synchronized (waitObj){
            System.out.println("waitObj notity ...");
            waitObj.notify();
        }
    }

    /**
     * <p>
     *    1. LockSupport不需要在同步代码块里 。所以线程间也不需要维护一个共享的同步对象了，实现了线程间的解耦。说明：park和wait的区别。wait让线程阻塞前，必须通过synchronized获取同步锁。
     *    2. unpark函数可以先于park调用，所以不需要担心线程间的执行的先后顺序。
     * </p>
     * @throws InterruptedException
     */
    static void lockSupportPark() {
        Thread A = new Thread(()->{
            int sum = 0;
            for (int i=0;i<10;i++){
                sum +=i;
            }
            LockSupport.park();
            System.out.println(sum);
        });
        A.start();
        // 不需要等待结果执行完成后阻塞。
        // Thread.sleep(1000);
        LockSupport.unpark(A);
    }
}
